﻿//-----------------------------------------------------------------------
// <copyright file="AsAnInstallationOperatorIShouldRunTheSetupWizardAndBeAbleToLogOnAsDistributorAdministratorToTheDashboard.cs" company="OOHM PROCESSAMENTO DE DADOS LTDA.">
//     Microsoft Public License (MS-PL) This license governs use of the accompanying
//     software. If you use the software, you accept this license. If you do not
//     accept the license, do not use the software. 1. Definitions The terms "reproduce,"
//     "reproduction," "derivative works," and "distribution" have the same meaning
//     here as under U.S. copyright law. A "contribution" is the original software,
//     or any additions or changes to the software. A "contributor" is any person
//     that distributes its contribution under this license. "Licensed patents"
//     are a contributor's patent claims that read directly on its contribution.
//     2. Grant of Rights (A) Copyright Grant- Subject to the terms of this license,
//     including the license conditions and limitations in section 3, each contributor
//     grants you a non-exclusive, worldwide, royalty-free copyright license to
//     reproduce its contribution, prepare derivative works of its contribution,
//     and distribute its contribution or any derivative works that you create.
//     (B) Patent Grant- Subject to the terms of this license, including the license
//     conditions and limitations in section 3, each contributor grants you a non-exclusive,
//     worldwide, royalty-free license under its licensed patents to make, have
//     made, use, sell, offer for sale, import, and/or otherwise dispose of its
//     contribution in the software or derivative works of the contribution in
//     the software. 3. Conditions and Limitations (A) No Trademark License- This
//     license does not grant you rights to use any contributors' name, logo, or
//     trademarks. (B) If you bring a patent claim against any contributor over
//     patents that you claim are infringed by the software, your patent license
//     from such contributor to the software ends automatically. (C) If you distribute
//     any portion of the software, you must retain all copyright, patent, trademark,
//     and attribution notices that are present in the software. (D) If you distribute
//     any portion of the software in source code form, you may do so only under
//     this license by including a complete copy of this license with your distribution.
//     If you distribute any portion of the software in compiled or object code
//     form, you may only do so under a license that complies with this license.
//     (E) The software is licensed "as-is." You bear the risk of using it. The
//     contributors give no express warranties, guarantees or conditions. You may
//     have additional consumer rights under your local laws which this license
//     cannot change. To the extent permitted under your local laws, the contributors
//     exclude the implied warranties of merchantability, fitness for a particular
// </copyright>
//-----------------------------------------------------------------------
namespace AbstractSaaSTemplate.Utility.Tests{    using System.Threading;
    using System.Web.Mvc;
    using AbstractSaaSTemplate.Kernel.Factories;
    using AbstractSaaSTemplate.UI.Web.Controllers;
    using AbstractSaaSTemplate.UI.Web.Models.Install;
    using AbstractSaaSTemplate.UI.Web.Models.User;
    using AbstractSaaSTemplate.Utility.Tests.UI.Utilities;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// @13453151
    /// Como um Operador de Instalação, eu gostaria de configurar
    /// o e-mail do administrador, servidor de smtp e licença e
    /// ter a aplicação instalada e pronta para usuários Staff
    /// operarem o módulo Admin.
    /// </summary>
    [TestClass]
    public class TestFor0013453151
    {
        #region Configuration

        /* ========================================================
         * CONFIGURAÇÃO
         * =========================================================
         * A seguir, definiremos os parâmetros que serão utilizados pelo
         * teste. Estes parâmetros serão utilizados durante a execução
         * do teste para causar um estímulo esperado resultando em um
         * estado final esperado.
         * ========================================================= */

        /// <summary>
        /// Defines the encryption passphrase for the software.
        /// </summary>
        public const string EncryptionPassphrase = "LONELY LONELY LONELY LONELY LONELY LONELY. NOT REALLY A PROBLEM :)";
        
        /// <summary>
        /// Defines the distributor administrator e-mail address.
        /// </summary>
        public const string StaffEmail = "testeroohm@gmail.com";

        /// <summary>
        /// Defines the distributor administrator password.
        /// </summary>
        public const string SmtpPassword = "soaDapd9";

        /// <summary>
        /// Defines the distributor administrator host.
        /// </summary>
        public const string SmtpHost = "smtp.gmail.com";

        /// <summary>
        /// Defines the distributor administrator port.
        /// </summary>
        public const int SmtpPort = 587;

        /// <summary>
        /// Defines the distributor administrator ssl requirement.
        /// </summary>
        public const bool SmtpRequiresSsl = true;

        /// <summary>
        /// Defines the login for the smtp.
        /// </summary>
        public const string SmtpLogin = "testeroohm@gmail.com";

        /// <summary>
        /// Defines the default name use as sender of e-mails.
        /// </summary>
        public const string EmailDefaultSenderName = "testeroohm";

        /// <summary>
        /// Defines the default e-mail address as sender of e-mails.
        /// </summary>
        public const string EmailDefaultSenderEmailAddress = "testeroohm@gmail.com";

        /// <summary>
        /// Defines the base app domain.
        /// </summary>
        public const string BaseAppDomain = "{0}.oohm.com";

        /// <summary>
        /// Defines the connection string
        /// </summary>
        public const string ConnectionString = "metadata=res://*/Repositories.Core.ConnectorForSQL2K8.csdl|res://*/Repositories.Core.ConnectorForSQL2K8.ssdl|res://*/Repositories.Core.ConnectorForSQL2K8.msl;provider=System.Data.SqlClient;provider connection string=\"Data Source=(local)\\SQLEXPRESS;Initial Catalog=AbstractSaaSTemplate_ExampleDB;Integrated Security=True;MultipleActiveResultSets=True\"";

        /// <summary>
        /// Defines the root path for blobs.
        /// </summary>
        public const string BlobsRootPath = @"C:\Temp\Tests\AbstractSaaSTemplate";

        /// <summary>
        /// Defines the root url for blobs.
        /// </summary>
        public const string BlobsBaseUrl = @"http://localhost/AbstractSaaSTemplate/blobs/";

        #endregion

        #region Flags

        /// <summary>
        /// Gets a value indicating whether the installation has completed.
        /// </summary>
        public static bool HasBeenInstalled { get; private set; }

        #endregion

        /// <summary>
        /// Ensures that the installation procedure has run.
        /// </summary>
        public static void EnsureInstallationIsComplete()
        {
            if (!HasBeenInstalled)
            {
                var test = new TestFor0013453151();
                test.AsAnInstallationOperatorIShouldRunTheSetupWizardAndBeAbleToLogOnAsDistributoAttemptrAdministratorToTheDashboard();
            }
        }
        
        /// <summary>
        /// @13453151
        /// Como um Operador de Instalação, eu gostaria de configurar
        /// o e-mail do administrador, servidor de smtp e licença e
        /// ter a aplicação instalada e pronta para usuários Staff
        /// operarem o módulo Admin.
        /// Passos da Mímica --------------------------------------------------
        /// 1. Execução do passo 1 (Step1) do Assistente de Instalação (Setup/Install/Index)
        /// 2. Execução do passo 2 (Step2) do Assistente de Instalação (Setup/Install/Index)
        /// Resultado Esperado ------------------------------------------------
        /// 1. Checar se é possível fazer o logon com o e-mail usado no setup e a senha gerada pelo passo 2
        /// 2. Checar se, após autenticado, o usuário consegue acessar o dashboard do staff (área Admin)
        /// </summary>
        [TestMethod]
        public void AsAnInstallationOperatorIShouldRunTheSetupWizardAndBeAbleToLogOnAsDistributoAttemptrAdministratorToTheDashboard()
        {
            if (HasBeenInstalled)
            {
                return;
            }
            
            /* =========================================================
             * PREPARAÇÃO
             * =========================================================
             * Este teste chama algumas Actions de alguns Controllers que
             * tentam fazer a mimca de um uso específico do usuário. Estes
             * Controllers também prepcisam ser preparados (MockForTesting)
             * para serem executados dentro do contexto do Unit Test.
             * ========================================================= */
            var factory = new ApplicationFactory();
            var installController = factory.CreateControllerFor<InstallController>();
            var dashboardController = factory.CreateControllerFor<DashboardController>();
            var userController = factory.CreateControllerFor<UserController>();

            installController.MockForTesting("Index", HttpVerbs.Get);
            dashboardController.MockForTesting("Index", HttpVerbs.Get);
            userController.MockForTesting("LogOn", HttpVerbs.Post);

            /* =========================================================
             * EXECUÇÃO DA MÍMICA
             * =========================================================
             * Os procedimentos executados à seguir são uma mímica dos
             * procedimentos executados pelos usuários no uso real do sistema.
             * ========================================================= */

            // 1. Execução do passo 1 (Step1) do Assistente de Instalação (Setup/Install/Index)
            PerformInstallationStep1(
                installController,
                EncryptionPassphrase,
                StaffEmail);

            // 2. Execução do passo 2 (Step2) do Assistente de Instalação (Setup/Install/Index)
            var generadtedPassword = PerformInstallationStep2(
                installController,
                SmtpHost,
                SmtpPort,
                SmtpRequiresSsl,
                SmtpLogin,
                SmtpPassword,
                EmailDefaultSenderName,
                EmailDefaultSenderEmailAddress,
                BaseAppDomain);

            /* =========================================================
             * CHECAGEM DE RESULTADO ESPERADO
             * =========================================================
             * Abaixo, iremos fazer as checagens de cada resultado esperado.
             * ========================================================= */
            
            // 1. Checar se é possível fazer o logon com o e-mail usado no setup e a senha gerada pelo passo 2
            ExpectStaffAdministratorToLogOnWithCredentials(
                userController,
                StaffEmail,
                generadtedPassword);

            // 2. Checar se, após autenticado, o usuário consegue acessar o dashboard do staff (área Admin)
            ExpectStaffAdministratorToHaveAccessToDashboard(
                dashboardController);

            // Flags as installed
            HasBeenInstalled = true;
        }

        #region Performs

        /// <summary>
        /// 1. Execução do passo 1 (Step1) do Assistente de Instalação (Setup/Install/Index)
        /// </summary>
        /// <param name="controller">An instance of the InstallController</param>
        /// <param name="encryptionPassphrase">The encryption passphrase that will be used to cypher secured strings in the application.</param>
        /// <param name="staffEmail">The e-mail which will be used by the installation.</param>
        private static void PerformInstallationStep1(
            InstallController controller,
            string encryptionPassphrase,
            string staffEmail)
        {
            // Arrange
            var input = new Step1Form();
            input.StaffAdminEmailAddress = staffEmail;
            input.EncryptionPassphrase = encryptionPassphrase;
            input.ConnectionString = ConnectionString;
            input.BlobsRootPath = BlobsRootPath;
            input.BlobsBaseLocation = BlobsBaseUrl;

            // Act
            var result = controller.Step1(input);
            var viewResult = result as ViewResult;
        }

        /// <summary>
        /// 2. Execução do passo 2 (Step2) do Assistente de Instalação (Setup/Install/Index)
        /// </summary>
        /// <param name="controller">An instance of the InstallController</param>
        /// <param name="smtpHost">The smtp host which will set for the installation</param>
        /// <param name="smtpPort">The port which will set for the installation</param>
        /// <param name="smtpRequiresSsl">The requirement for Ssl which will set for the installation</param>
        /// <param name="smtpLogin">The login for the smtp which will set for the installation</param>
        /// <param name="smtpPassword">The password for smtp which will set for the installation</param>
        /// <param name="emailDefaultSenderName">The name used by the default e-mail account which will set for the installation</param>
        /// <param name="emailDefaultSenderEmailAddress">The e-mail address used by the default e-mail which will set for the installation</param>
        /// <param name="baseAppDomain">The base app domain, used to identify the tenant by its key.</param>
        /// <returns>Returns a password generated in the last step.</returns>
        private static string PerformInstallationStep2(
            InstallController controller,
            string smtpHost,
            int smtpPort,
            bool smtpRequiresSsl,
            string smtpLogin,
            string smtpPassword,
            string emailDefaultSenderName,
            string emailDefaultSenderEmailAddress,
            string baseAppDomain)
        {
            // Arrange 
            var input = new Step2Form();
            input.SmtpHost = smtpHost;
            input.SmtpPort = smtpPort.ToString();
            input.SmtpLogin = smtpLogin;
            input.SmtpPassword = smtpPassword;
            input.SmtpRequiresSsl = smtpRequiresSsl;
            input.EmailDefaultSenderName = emailDefaultSenderName;
            input.EmailDefaultSenderEmailAddress = emailDefaultSenderEmailAddress;
            input.AppBaseDomain = baseAppDomain;

            // Act
            var result = controller.Step2(input);

            // Assert
            Assert.IsInstanceOfType(result, typeof(PartialViewResult));
            var model = (result as PartialViewResult).Model as string;
            Assert.IsFalse(string.IsNullOrWhiteSpace(model));
            return model;
        }

        #endregion

        #region Expects

        /// <summary>
        /// 1. Checar se é possível fazer o logon com o e-mail usado no setup e a senha gerada pelo passo 3
        /// </summary>
        /// <param name="userController">An instance of UserController which will be used for logon</param>
        /// <param name="staffEmail">The e-mail address which will be checked against the setup.</param>
        /// <param name="generadtedPassword">The password which will be used for authentication (to check if the member was properly setup)</param>
        private static void ExpectStaffAdministratorToLogOnWithCredentials(
            UserController userController,
            string staffEmail,
            string generadtedPassword)
        {
            var input = new LogOnForm();
            input.Login = staffEmail;
            input.Password = generadtedPassword;
            input.RememberMe = false;

            var actionResult = userController.LogOn(input, string.Empty);

            Assert.IsInstanceOfType(
                actionResult,
                typeof(RedirectToRouteResult));

            var redirectToRouteResult = actionResult as RedirectToRouteResult;
            var actionName = redirectToRouteResult.RouteValues["action"] as string;
            var controllerName = redirectToRouteResult.RouteValues["controller"] as string;
            var activeMemberEmail = userController.ServiceForMembership.ActiveUser.EmailAddress;

            Assert.AreEqual("index", actionName.ToLower());
            Assert.AreEqual("home", controllerName.ToLower());
            Assert.AreEqual(staffEmail, activeMemberEmail);
        }

        /// <summary>
        /// 2. Checar se, após autenticado, o usuário consegue acessar o dashboard do staff (área Admin)
        /// </summary>
        /// <param name="dashboardController">An instance of the DashboardController</param>
        private static void ExpectStaffAdministratorToHaveAccessToDashboard(
            DashboardController dashboardController)
        {
            var result = dashboardController.Index();
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = result as ViewResult;
            Assert.AreEqual<string>("Index", viewResult.ViewName);
        }

        #endregion
    }
}


